#!/usr/bin/python3
# encoding: utf-8
import time

import serial
import modbus_tk
import modbus_tk.modbus_rtu as modbus_rtu
import threading

logger = modbus_tk.utils.create_logger("console")

# configs
ser_conf_dict = {"port": "COM6", "baudrate": 9600, "bytesize": 8, "parity": 'N', "stopbits": 1, "timeout": None}
cmd_history_list = ['02 02 00 00 00 01 B9 F9']
options_parity = ['N', 'E', 'O', 'M', 'S', 'Prompt: N=无, E=偶, O=奇, M=Mark, S=空格']
options_port = ["/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3"]
hex_display = 1

auto_send = {"period-sec": 1}


class SerialCmd():
    def __init__(self, ser_conf=None):
        self.ser_conf = ser_conf
        self.__serial = None
        self.recv_thread = None

    # 打印串口当前配置信息
    def print_ser_conf(self):
        print("\nSerial port information:")
        for index, key in enumerate(self.ser_conf):
            print("\t{}: {}={}".format(index, key, self.ser_conf[key]))

    # 修改串口配置并保存到配置文件
    def modify_save_conf(self):
        index = input("Input a num to set, none to open serial:\n")
        if not index:
            return False
        ser_conf_keys = list(ser_conf_dict.keys())
        key = ser_conf_keys[int(index)]
        new_value = ""
        if key == "port":
            for i, num in enumerate(options_port):
                print("\t{}: {}".format(i, options_port[i]))
            new_value = options_port[int(input("select the digital to set:"))]
        elif key == "parity":
            for i, num in enumerate(options_parity):
                print("\t{}: {}".format(i, options_parity[i]))
            new_value = options_parity[int(input("select the digital to set:"))]
        else:
            print("Input new value to modify {}={}:".format(key, ser_conf_dict[key]))
            new_value = input()
        if not new_value:
            return False
        ser_conf_dict[key] = new_value
        self.print_ser_conf()
        return True

    def set_serial_conf(self):
        self.print_ser_conf()
        while True:
            try:
                if not self.modify_save_conf():
                    break
            except Exception as e:
                print("\n**ERR: {}".format(e))
        print("exit serial conf setting!")

    def open_port(self):
        if self.is_open():
            self.close_port()
        try:
            self.__serial = serial.Serial(port=self.ser_conf['port'], baudrate=self.ser_conf['baudrate'],
                                          bytesize=self.ser_conf['bytesize'], parity=self.ser_conf['parity'],
                                          stopbits=self.ser_conf['stopbits'], timeout=self.ser_conf['timeout'])
        except Exception as e:
            print("**ERR: " + e)
            return
        print("Port={} is opened.".format(self.__serial.port))

    def close_port(self):
        if self.is_open() and self.__serial:
            self.__serial.close()

    def send_data(self, data):
        if not self.is_open():
            print("Serial port is not opened.")
            return False
        output = ""
        for d in data:
            output += "{:02X} ".format(d)
        print("[S]: ", output)
        if hex_display:
            self.__serial.write(bytes(data))
        else:
            self.__serial.write("".join(data))
        self.refresh_history_cmd(data)
        self.__serial.flush()

    def is_open(self):
        if self.__serial and self.__serial.isOpen:
            return self.__serial.isOpen
        print("Serial port is not opened.")
        return False

    def get_input_cmd(self):
        input_s = input("\nInput your cmd, null for last cmd: {}\n".format(cmd_history_list[-1]))
        if not input_s:
            input_s = cmd_history_list[-1]
            # return None
        send_list = []
        if hex_display:
            # hex发送 strip()去除首尾空格
            input_s = input_s.strip()

            while input_s != '':
                try:
                    num = int(input_s[0:2], 16)
                except ValueError:
                    print('**ERR: Please input hex data, splited with a space!')
                    return None
                input_s = input_s[2:].strip()
                send_list.append(num)
            return send_list
        else:
            # ascii发送
            input_s = (input_s + '\r\n').encode('utf-8')
            send_list[input_s]
        return send_list

    def recv_data(self):
        while self.is_open():
            count = 0
            data = ''
            while True:
                time.sleep(0.1)
                num = self.__serial.inWaiting()
                output = ""
                if num > 0:
                    data = self.__serial.read(num)
                    # hex显示
                    if hex_display:
                        out_s = ''
                        for i in range(0, len(data)):
                            out_s = out_s + '{:02X}'.format(data[i]) + ' '
                        output = out_s.split()
                    print("[R]: ", out_s)

    def do_commond_interactive(self):
        self.open_port()
        if not self.is_open():
            return
        self.recv_thread = threading.Thread(target=self.recv_data, args=())
        self.recv_thread.start()
        print("Receive thread was started.")
        while True:
            data = self.get_input_cmd()
            self.send_data(data)
            time.sleep(0.5)

    def refresh_history_cmd(self, data):
        pass


class SerialConf(object):
    def __init__(self, ser_conf=None):
        self.ser_conf = ser_conf

    def reload_conf_file(self):
        pass

    def update_conf_file(self, ser_conf):
        pass


def main():
    serial_conf = SerialConf(ser_conf_dict)
    serial_conf.reload_conf_file()
    serial_cmd = SerialCmd(serial_conf.ser_conf)
    serial_cmd.set_serial_conf()
    serial_conf.update_conf_file(serial_cmd.ser_conf)
    print("Entering interactive command...")
    serial_cmd.do_commond_interactive()


if __name__ == "__main__":
    main()
